package titan.wizards.newTitanProject;

import java.io.File;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import titan.messages.Messages;
import titan.utils.FileUtils;
import titan.utils.SWTUtils;
import titan.utils.StatusBar;
import titan.utils.SVN;
import titan.utils.TextValidator;
import titan.utils.TitanUtils;
import titan.wizards.BaseTitanPage;

/**
 * At this page the project location as well as the core and repository location
 * associated to this project can be defined. It's possible to download the core
 * and repository from a SVN Server, if the user doesn't have it
 */
public class LocalPage extends BaseTitanPage {
	Text projectFolder;
	Text projectPath;
	Text projectLocation;
	Text coreLocation;
	Text reposLocation;
	//Internal data
	private boolean isDialogOpen;
	private Button projectLocationButton;
	private Button coreLocationButton;
	private Button reposLocationButton;
	private Button coreSVNButton;
	private Button reposSVNButton;
	private StatusBar progressBar;
	//Status
	private IStatus projectFolderStatus;
	private IStatus projectPathStatus;
	private IStatus coreLocationStatus;
	private IStatus reposLocationStatus;
	//private static final char[] ILLEGAL_CHARS = { '/', '`', '?', '*', '\\', '<', '>', '|', '\"', ':', ';' };

	protected LocalPage() {
		super(Messages.Location);
		setTitle(Messages.Location);
		setDescription(Messages.LocalPage_desc);
		IStatus status = new Status(IStatus.OK, "default", ""); //$NON-NLS-1$ //$NON-NLS-2$
		projectFolderStatus = projectPathStatus = coreLocationStatus = reposLocationStatus = status;
		isDialogOpen = false;
	}

	/**
	 * {@inheritDoc}
	 */
	public void createControl(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		final int MAX_GD = 4;
		GridLayout layout = new GridLayout(MAX_GD, false);
		layout.verticalSpacing = 12;
		composite.setLayout(layout);
		setControl(composite);
		GridData gd = new GridData(330, 13);

		MouseTrackAdapter mouseExit = new MouseTrackAdapter() {
			public void mouseExit(MouseEvent e) {
			    applyStatus(pageStatus);
			}
		};

		new Label(composite, SWT.NONE).setText(Messages.ProjectFolder+":"); //$NON-NLS-1$
		projectFolder = new Text(composite, SWT.BORDER | SWT.SINGLE);
		gd.horizontalAlignment = SWT.FILL;
		gd.horizontalSpan = MAX_GD - 1;
		projectFolder.setLayoutData(gd);
		projectFolder.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent event) {
				String newName = projectFolder.getText();
				IStatus status = new Status(IStatus.OK, "projectFolder", ""); //$NON-NLS-1$ //$NON-NLS-2$
				if(!newName.isEmpty()) { //$NON-NLS-1$
					if(!TextValidator.isValidFileName(newName)) //$NON-NLS-1$ //$NON-NLS-2$
						status = new Status(IStatus.ERROR, "projectFolder", //$NON-NLS-1$
								Messages.InvalidValue+Messages.ProjectFolder_ext);
					else {
						if(!projectLocation.getText().isEmpty()) //$NON-NLS-1$
							projectPathChanged(projectLocation.getText());
						//Suggest name to projectPage
						ProjectPage pp = ((TitanWizard)getWizard()).projectPage;
						pp.suggestName(newName);
					}
				}
				applyStatus(status); //$NON-NLS-1$ //$NON-NLS-2$
				updateButtons();
			}
		});
		projectFolder.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "projectFolder", Messages.ProjectFolder_desc) ); //$NON-NLS-1$
			}
		});
		projectFolder.addMouseTrackListener(mouseExit);

		new Label(composite, SWT.NONE).setText(Messages.ProjectLocation+":"); //$NON-NLS-1$
		projectLocation = new Text(composite, SWT.BORDER | SWT.SINGLE);
		gd = new GridData(300, 13);
		gd.horizontalAlignment = SWT.FILL;
		gd.horizontalSpan = MAX_GD - 2;
		projectLocation.setLayoutData(gd);
		projectLocation.addListener(SWT.MouseDown, new Listener () {
			public void handleEvent(Event e) {
				projectLocation.setCapture(false);
			}
		});
		projectLocation.addListener(SWT.FocusIn, new Listener () {
			public void handleEvent(Event e) {
					if(isDialogOpen){
						isDialogOpen = false;
						projectLocationButton.setFocus();
					}
					else {
						isDialogOpen = true;
						projectLocationSelected();
					}
			}
		});
		projectLocation.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "projectLocation", Messages.ProjectLocation_desc2) ); //$NON-NLS-1$
			}
		});
		projectLocation.addMouseTrackListener(mouseExit);
		projectLocationButton = new Button(composite, SWT.PUSH);
		projectLocationButton.setText(Messages.Browse);
		projectLocationButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				projectLocationSelected();
			}
		});

		new Label(composite, SWT.NONE).setText(Messages.ProjectPath+":"); //$NON-NLS-1$
		projectPath = new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.READ_ONLY);
		gd = new GridData();
		gd.horizontalSpan = MAX_GD - 1;
		gd.horizontalAlignment = SWT.FILL;
		projectPath.setLayoutData(gd);

		new Label(composite, SWT.NONE);
		coreSVNButton = new Button(composite, SWT.PUSH);
		coreSVNButton.setText(Messages.downloadCore);
		coreSVNButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if(coreLocation.getText().isEmpty()) //$NON-NLS-1$
					coreLocationSelected();
				progressBar = new StatusBar(getShell());
				progressBar.open();
				getShell().getDisplay().asyncExec( new Runnable() {  public void run() {
					try {
						SVN svn = new SVN(coreLocation.getText(), "https://svn.ledes.net/titan/core/"); //$NON-NLS-1$
						svn.checkout(progressBar);
						applyStatus(new Status(IStatus.OK, "coreLocation", "")); //$NON-NLS-1$ //$NON-NLS-2$
					} catch (Exception e) {
						MessageDialog.openError(getShell(), Messages.Error, e.getMessage());
					}
					progressBar.close();
				}});
			}
		});
		coreSVNButton.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "coreSVN", Messages.downloadCore_desc) ); //$NON-NLS-1$
			}
		});
		coreSVNButton.addMouseTrackListener(mouseExit);

		reposSVNButton = new Button(composite, SWT.PUSH);
		reposSVNButton.setText(Messages.downloadRepos);
		reposSVNButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if(reposLocation.getText().isEmpty()) //$NON-NLS-1$
					reposLocationSelected();
				progressBar = new StatusBar(getShell());
				progressBar.open();
				getShell().getDisplay().asyncExec( new Runnable() {  public void run() {
					try {
						SVN svn = new SVN(reposLocation.getText(), "https://svn.ledes.net/titan/repository/"); //$NON-NLS-1$
						svn.checkout(progressBar);
						applyStatus(new Status(IStatus.OK, "reposLocation", "")); //$NON-NLS-1$ //$NON-NLS-2$
					} catch (Exception e) {
						MessageDialog.openError(getShell(), Messages.Error, e.getMessage());
					}
					progressBar.close();
				}});
			}
		});
		reposSVNButton.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "reposSVN", Messages.downloadRepos_desc) ); //$NON-NLS-1$
			}
		});
		reposSVNButton.addMouseTrackListener(mouseExit);
		new Label(composite, SWT.NONE);

		new Label(composite, SWT.NONE).setText(Messages.CoreLocation+":"); //$NON-NLS-1$
		coreLocation = new Text(composite, SWT.BORDER | SWT.SINGLE);
		gd = new GridData();
		gd.horizontalSpan = MAX_GD - 2;
		gd.horizontalAlignment = SWT.FILL;
		coreLocation.setLayoutData(gd);
		coreLocation.addListener(SWT.MouseDown, new Listener () {
			public void handleEvent(Event e) {
				coreLocation.setCapture(false);
			}
		});
		coreLocation.addListener(SWT.FocusIn, new Listener () {
			public void handleEvent(Event e) {
					if(isDialogOpen){
						isDialogOpen = false;
						coreLocationButton.setFocus();
					}
					else {
						isDialogOpen = true;
						coreLocationSelected();
					}
			}
		});
		coreLocation.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "coreLocation", Messages.CoreLocation_desc)); //$NON-NLS-1$
			}
		});
		coreLocation.addMouseTrackListener(mouseExit);
		coreLocationButton = new Button(composite, SWT.PUSH);
		coreLocationButton.setText(Messages.Browse);
		coreLocationButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				coreLocationSelected();
			}
		});

		new Label(composite, SWT.NONE).setText(Messages.ReposLocation+":"); //$NON-NLS-1$
		reposLocation = new Text(composite, SWT.BORDER | SWT.SINGLE);
		gd = new GridData();
		gd.horizontalAlignment = SWT.FILL;
		gd.horizontalSpan = MAX_GD - 2;
		reposLocation.setLayoutData(gd);
		reposLocation.addListener(SWT.MouseDown, new Listener () {
			public void handleEvent(Event e) {
				reposLocation.setCapture(false);
			}
		});
		reposLocation.addListener(SWT.FocusIn, new Listener () {
			public void handleEvent(Event e) {
					if(isDialogOpen){
						isDialogOpen = false;
						reposLocationButton.setFocus();
					}
					else {
						isDialogOpen = true;
						reposLocationSelected();
					}
			}
		});
		reposLocation.addListener(SWT.MouseEnter, new Listener () {
			public void handleEvent(Event e) {
			    applyStatus(new Status(IStatus.INFO, "reposLocation", Messages.ReposLocation_desc)); //$NON-NLS-1$
			}
		});
		reposLocation.addMouseTrackListener(mouseExit);
		reposLocationButton = new Button(composite, SWT.PUSH);
		reposLocationButton.setText(Messages.Browse);
		reposLocationButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				reposLocationSelected();
			}
		});
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean canFlipToNextPage() {
		if(projectFolder.getText().isEmpty() || projectLocation.getText().isEmpty() || coreLocation.getText().isEmpty() //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			|| reposLocation.getText().isEmpty() || pageStatus.getSeverity() == IStatus.ERROR) //$NON-NLS-1$
			return false;
		return true;
	}

	/**
	 * It validates the selected core location and applies the status associated
	 */
	private void testCoreLocation() {
		Status status = new Status(IStatus.OK, "coreLocation", ""); //$NON-NLS-1$ //$NON-NLS-2$
		try {
			if(!TitanUtils.testTitanCore(coreLocation.getText())) //$NON-NLS-1$
				status = new Status(IStatus.ERROR, "coreLocation", Messages.CoreNotValid); //$NON-NLS-1$
		} catch (Exception e) {
			status = new Status(IStatus.ERROR, "coreLocation", e.getMessage()); //$NON-NLS-1$
		}
		applyStatus(status);
	}

	/**
	 * It validates the selected repository location and applies the status associated
	 */
	private void testReposLocation() {
		Status status = new Status(IStatus.OK, "reposLocation", ""); //$NON-NLS-1$ //$NON-NLS-2$
		try {
			if(!TitanUtils.testTitanRepos(reposLocation.getText())) //$NON-NLS-1$
				status = new Status(IStatus.ERROR, "reposLocation", Messages.ReposNotValid); //$NON-NLS-1$
		} catch (Exception e) {
			status = new Status(IStatus.ERROR, "reposLocation", e.getMessage()); //$NON-NLS-1$
		}
		applyStatus(status);
	}

	/**
	 * This function is called when project path was selected.
	 * It validates the selected project and applies the status associated
	 * @param fn - the path to validate
	 */
	private void projectPathChanged(String fn) {
		if (fn != null) {
			IStatus status = new Status(IStatus.OK, "projectPath", ""); //$NON-NLS-1$ //$NON-NLS-2$
			projectLocation.setText(fn);
			if (!fn.endsWith(File.separator))
				fn += File.separator; //$NON-NLS-1$
			projectPath.setText(fn + projectFolder.getText());
			try {
				if(!FileUtils.isEmptyFolder(projectPath.getText()))
				    status = new Status(IStatus.WARNING, "projectPath", //$NON-NLS-1$
				    		Messages.ProjFolderNEmpty+Messages.ContentWillBeReplaced);
			} catch (Exception e) {
				status = new Status(IStatus.ERROR, "projectPath", e.getMessage()); //$NON-NLS-1$
			}
			applyStatus(status);
		}
	}

	/**
	 * This function is called when project location was selected
	 */
	private void projectLocationSelected(){
		projectPathChanged(SWTUtils.openFolderDialog(getShell()));
		updateButtons();
	}

	/**
	 * This function is called when core location was selected.
	 * It calls {@link LocalPage#testCoreLocation() testCoreLocation} to validate the selected location
	 */
	private void coreLocationSelected(){
		String fn = SWTUtils.openFolderDialog(getShell());
		if (fn != null){
			coreLocation.setText(fn);
			testCoreLocation();
		}
		updateButtons();
	}

	/**
	 * This function is called when repos location was selected.
	 * It calls {@link LocalPage#testReposLocation() testReposLocation} to validate the selected location
	 */
	private void reposLocationSelected(){
		String fn = SWTUtils.openFolderDialog(getShell());
		if (fn != null){
			reposLocation.setText(fn);
			testReposLocation();
		}
		updateButtons();
	}

	/**
	 * {@inheritDoc}
	 */
	protected void applyStatus(IStatus new_status) {
		if(new_status.getSeverity() != IStatus.INFO){
			String plugin = new_status.getPlugin();
			if(plugin == "projectFolder") //$NON-NLS-1$
				projectFolderStatus = new_status;
			else if(plugin == "coreLocation") //$NON-NLS-1$
				coreLocationStatus = new_status;
			else if(plugin == "projectPath") //$NON-NLS-1$
				projectPathStatus = new_status;
			else
				reposLocationStatus = new_status;
		}
		applyPageStatus(new_status);
	}

	/**
	 * {@inheritDoc}
	 */
	protected IStatus greaterStatus() {
		IStatus greater = coreLocationStatus;
		if(greater.getSeverity() < projectFolderStatus.getSeverity())
			greater = projectFolderStatus;
		if(greater.getSeverity() < projectPathStatus.getSeverity())
			greater = projectPathStatus;
		if(greater.getSeverity() < reposLocationStatus.getSeverity())
			greater = reposLocationStatus;
		return greater;
	}
}